/**
 * Copyright (C) 2014 Domon.Xie All Rights Reserved.
 *
 * ClassPathModelScanner.java
 * 2014年10月19日 下午6:38:49
 */
package org.domon.mybatis.model;

import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;

import org.domon.mybatis.annotation.Table;
import org.domon.mybatis.bind.ModelRegistry;
import org.domon.mybatis.sql.DbNameGenerator;
import org.domon.mybatis.sql.support.AnnotationDbNameGenerator;
import org.domon.mybatis.sql.support.DefaultDbNameGenerator;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;

/**
 * @author Domon.Xie
 * 
 */
public class ClassPathModelScanner extends ClassPathBeanDefinitionScanner {

    /**
     * 扫描到Mybatis model 增加bean name前缀
     */
    final static String MODEL_BEAN_NAME_PREFIX = "Mybatis_Bean_";

    private DbNameGenerator dbNameGenerator = new DefaultDbNameGenerator();

    public void setDbNameGenerator(DbNameGenerator dbNameGenerator) {
        this.dbNameGenerator = dbNameGenerator == null ? new DefaultDbNameGenerator() : dbNameGenerator;
    }

    /**
     * @param registry
     */
    public ClassPathModelScanner(BeanDefinitionRegistry registry) {
        super(registry, false);
    }

    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
        
        // SQL注册中心
        ModelRegistry modelRegistry = new ModelRegistry();
        // ModelRegistry's bean definition
        GenericBeanDefinition definition = new GenericBeanDefinition();
        definition.setBeanClass(ModelRegistry.class);
        // 注入SQL注册中心
        definition.getConstructorArgumentValues().addGenericArgumentValue(modelRegistry, ModelRegistry.class.getName());
        // 注入数据库名解析器
        definition.getConstructorArgumentValues().addGenericArgumentValue(dbNameGenerator, DbNameGenerator.class.getName());

        String beanName = new DefaultBeanNameGenerator().generateBeanName(definition, this.getRegistry());
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(definition, beanName);
        beanDefinitions.add(definitionHolder);
        registerBeanDefinition(definitionHolder, this.getRegistry());
        

        Set<BeanDefinitionHolder> holders = super.doScan(basePackages);
        if (holders.isEmpty()) {
            logger.warn("No MyBatis model was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
            for (BeanDefinitionHolder holder : holders) {
                GenericBeanDefinition candidate = (GenericBeanDefinition) this.getRegistry().getBeanDefinition(holder.getBeanName());
                this.getRegistry().removeBeanDefinition(holder.getBeanName());

                beanName = new AnnotationBeanNameGenerator().generateBeanName(candidate, this.getRegistry());
                if (checkCandidate(beanName, candidate)) {
                    beanDefinitions.add(new BeanDefinitionHolder(candidate, MODEL_BEAN_NAME_PREFIX + beanName));
                    registerBeanDefinition(holder, this.getRegistry());

                    try {
                        Class<?> beanClass = Class.forName(candidate.getBeanClassName());
                        if (logger.isDebugEnabled()) {
                            logger.debug("Generating bean's [" + holder.getBeanName() + " - " + candidate.getBeanClassName() + "] sql");
                        }

                        dbNameGenerator.initializeAllSQL(beanClass, modelRegistry);
                    } catch (ClassNotFoundException e) {
                        logger.error("Bean class name [" + candidate.getBeanClassName() + "] does not exist", e);
                    }
                }
            }
        }
        return beanDefinitions;
    }

    public void registerFilters() {

        if (dbNameGenerator instanceof AnnotationDbNameGenerator) {
            addIncludeFilter(new AnnotationTypeFilter(Table.class));
            return;
        }

        addIncludeFilter(new TypeFilter() {

            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                ClassMetadata classMetadata = metadataReader.getClassMetadata();
                
                if (!classMetadata.isAbstract() && !classMetadata.isInterface()) {
                    // 扫描T开头的非接口非抽象类，默认它们为数据库表对应实体类
                    if (ClassUtils.getShortName(classMetadata.getClassName()).startsWith("T")) {
                        return true;
                    }
                }
                return false;
            }
        });
    }
}
